package com.hicorp.segment.security.services;

import com.hicorp.segment.mapper.*;
import com.hicorp.segment.pojo.bo.UserForSecurityBO;
import com.hicorp.segment.pojo.po.BackendApi;
import com.hicorp.segment.pojo.po.Menu;
import com.hicorp.segment.pojo.po.Role;
import com.hicorp.segment.pojo.po.User;
import com.hicorp.segment.security.exception.RoleNotFoundException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 实现 UserDetailsService 接口，重写验证机制。
 *
 * @author 万一
 */
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private OperationMapper operationMapper;

    @Resource
    private BackendApiMapper backendApiMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        // 按前端传来的用户名查询用户.
        User user = userMapper.findByUserName(s);

        // 如果查询到的User为空.
        if (user == null) throw new UsernameNotFoundException("admin: " + s + "do not exist!");

        // 断言 查询到的 User存在, 但无账号或密码的情况.
        Assert.isTrue(user.getUsername() != null && !"".equals(user.getUsername()) && user.getPassword() != null, "Cannot pass null or empty values to constructor");

        // 所有可访问的页面
        List<Menu> menuList;

        // 所有的可访问的后台接口
        List<BackendApi> backendApiList;

        // spring security 权限验证List
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        // 开发人员专用可访问所有的页面 以及 api, 后期部署时需要删除掉.
        if (user.getId() == 1 && user.getUsername().equals("开发人员专用")) {
            menuList = menuMapper.selectAll();
        } else {
            // 查询该用户所有的角色
            List<Role> roleList;

            roleList = roleMapper.selectRoleListByUserId(user.getId());

            // 当未对用户赋角色时
            if (roleList.size() == 0) throw new RoleNotFoundException("管理员未对该用户授予任何角色!");

            List<Long> roleIdList = new ArrayList<>();
            roleList.forEach((Role role) -> roleIdList.add(role.getId()));

            // 通过角色ID获取用户菜单
            menuList = menuMapper.selectMenuListByRoleIdList(roleIdList);

//            // 通过角色ID获取用户可进行的操作功能
//            List<Operation> operationList = operationMapper.selectOperationListByRoleIdList(roleIdList);
//            List<Long> operationIdList = new ArrayList<>();
//            operationList.forEach((Operation operation) -> operationIdList.add(operation.getId()));
//
//            List<Long> menuIdList = new ArrayList<>();
//            // 当未给角色赋予菜单时
//            // if (menuList.size() == 0) throw new RoleNotFoundMenuException("该用户的某个角色未授予任何菜单访问权限!");
//            menuList.forEach((Menu menu) -> menuIdList.add(menu.getId()));
//
//            // 通过用户菜单id和用户可进行的操作的id获取可访问的后端api
//            // 菜单资源所访问的后端api
//            List<BackendApi> menuBackendApiList = backendApiMapper.selectBackendApiListByMenuIdList(menuIdList);
//
//            // 功能所访问的后端api
//            List<BackendApi> operationBackendApiList = backendApiMapper.selectBackendApiListByOperationIdList(operationIdList);
//            if (operationList.size() == 0) {
//                // 仅有菜单访问权, 没有任何的功能权限的时候.
//                backendApiList = menuBackendApiList;
//            } else {
//                // 利用流合并去重
//                backendApiList = Stream.of(menuBackendApiList, operationBackendApiList)
//                        .flatMap(Collection::stream)
//                        .distinct()
//                        .collect(Collectors.toList());
//            }
//
//            // 当 grantedAuthorities 无数据时
//            if (CollectionUtils.isEmpty(grantedAuthorities)) {
//                backendApiList.forEach(backendApi -> {
//                    if (backendApi != null && backendApi.getSummary() != null) {
//                        // 将权限信息添加到 GrantedAuthority 对象中，在后面权限验证时会使用 GrantedAuthority 对象。
//                        // 如无特殊需求，请选择设置了 unique 属性的列！！！
//                        GrantedAuthority grantedAuthority = new GrantedAuthorityImpl(backendApi.getApiPath(), backendApi.getMethod());
//                        grantedAuthorities.add(grantedAuthority);
//                    }
//                });
//            }
        }

        GrantedAuthority grantedAuthority1 = new GrantedAuthorityImpl("/**", "GET");
        grantedAuthorities.add(grantedAuthority1);
        GrantedAuthority grantedAuthority2 = new GrantedAuthorityImpl("/**", "POST");
        grantedAuthorities.add(grantedAuthority2);
        GrantedAuthority grantedAuthority3 = new GrantedAuthorityImpl("/**", "PUT");
        grantedAuthorities.add(grantedAuthority3);
        GrantedAuthority grantedAuthority4 = new GrantedAuthorityImpl("/**", "DELETE");
        grantedAuthorities.add(grantedAuthority4);

        // 将查询到的菜单转换成树形结构.
        List<Menu> userMenuList =
                menuList
                        .stream()
                        .filter(menu -> menu.getParentId() != null)
                        .filter(menu -> 1 == menu.getParentId())
                        .peek(menu -> menu.setChildren(getMenuChildren(menu, menuList)))
                        .collect(Collectors.toList());

        return new UserForSecurityBO(user.getUsername(), user.getPassword(), grantedAuthorities, user.getUserNo(), user.getStaffNo(), user.getAuthorityCode(), userMenuList);
    }

    // 生成树形结构的菜单.
    private List<Menu> getMenuChildren(Menu rootMenu, List<Menu> allMenuList) {
        return allMenuList
                .stream()
                .filter((menu) -> Objects.equals(rootMenu.getId(), menu.getParentId()))
                .collect(Collectors.toList());
    }
}
